package com.zj.ctrl;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zj.ctrl.HCNetSDK;


import com.zj.ctrl.PlayCtrl;
import com.zj.thread.MediaTransferFlvByFFmpeg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.NativeLongByReference;

/**
 * 工具类
 *
 * @author lirt
 */
@Component
public class SdkTool {
    @Value("${hkvis.ip}")
    public String IP;
    @Value("${hkvis.port}")
    public String PORT;
    @Value("${hkvis.userName}")
    public String USER_NAME;
    @Value("${hkvis.password}")
    public String PASSWORD;

    private Logger logger = LoggerFactory.getLogger(getClass());

    static HCNetSDK hCNetSDK = HCNetSDK.INSTANCE;
    static PlayCtrl playControl = PlayCtrl.INSTANCE;

    HCNetSDK.NET_DVR_DEVICEINFO_V30 m_strDeviceInfo;// 设备信息
    HCNetSDK.NET_DVR_IPPARACFG m_strIpparaCfg;// IP参数
    HCNetSDK.NET_DVR_CLIENTINFO m_strClientInfo;// 用户参数

    boolean bRealPlay;// 是否在预览.
    String m_sDeviceIP;// 已登录设备的IP地址

    NativeLong lUserID;// 用户句柄
    NativeLong lPreviewHandle;// 预览句柄
    NativeLongByReference m_lPort;// 回调预览时播放库端口指针

    NativeLong m_lDownloadHandle;// 下载句柄

    /**
     * 初始化
     */
    @PostConstruct
    public void initSDKTools() {
        lUserID = new NativeLong(-1);
        lPreviewHandle = new NativeLong(-1);
        m_lDownloadHandle = new NativeLong(-1);
        m_lPort = new NativeLongByReference(new NativeLong(-1));

        // 初始化资源配置
        boolean initSuc = hCNetSDK.NET_DVR_Init();
        if (!initSuc) {
            logger.error("初始化失败........................");
        }
        // 设置连接时间与重连时间
        hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
        hCNetSDK.NET_DVR_SetReconnect(10000, true);
    }

    @PreDestroy
    public void destorySDKTools() {
        // 先注销,在登录
        if (lUserID.intValue() > -1) {
            hCNetSDK.NET_DVR_Logout_V30(lUserID);
            lUserID = new NativeLong(-1);
        }
        // 释放SDK资源
        hCNetSDK.NET_DVR_Cleanup();
    }

    /**
     * 设备注册
     *
     * @param ip       IP地址
     * @param port     端口
     * @param userName 设备用户名
     * @param password 设备登录密码
     * @return RegistReturnType
     */
    public int deviceRegist(final String ip, final String port, final String userName, final String password) {

        if (StringUtils.isBlank(ip) || StringUtils.isBlank(port)
                || StringUtils.isBlank(userName) || StringUtils.isBlank(password)) {
            logger.error("nvr paras is null");
            return 2;
        }
        // 判断当前是否在预览
        if (bRealPlay) {
            return RegistReturnType.ERR_PLAY_ING;// "注册新用户请先停止当前预览";
        }

        // 先注销,在登录
        if (lUserID.intValue() > -1) {
            hCNetSDK.NET_DVR_Logout_V30(lUserID);
            lUserID = new NativeLong(-1);
        }

        // 注册(既登录设备)开始
        m_sDeviceIP = ip;
        short iPort = Integer.valueOf(port).shortValue();
        // 获取设备参数结构
        m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30();
        // 登录设备
        lUserID = hCNetSDK.NET_DVR_Login_V30(ip, iPort, userName, password, m_strDeviceInfo);
        // 获取用户id
        if (lUserID.intValue() == -1) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            logger.info("：注册失败,错误号：" + iErr);
            logger.info(hCNetSDK.NET_DVR_GetErrorMsg(m_lPort));
            m_sDeviceIP = "";// 登录未成功,IP置为空
            return RegistReturnType.ERR_REGIST_ERR;// "注册失败";
        }
        return RegistReturnType.SUCCESS;
    }

    public static void main(String[] args) {
        short iPort = Integer.valueOf("8000").shortValue();
        System.out.println(iPort);
    }


    public int deviceRegist() {
        return deviceRegist(IP, PORT, USER_NAME, PASSWORD);
    }

    /**
     * 获取设备通道
     */
    public List<Map<Integer, String>> getChannelList() {
        final List<Map<Integer, String>> devices = new ArrayList<Map<Integer, String>>();
        //
        IntByReference ibrBytesReturned = new IntByReference(0);// 获取IP接入配置参数
        boolean bRet = false;

        // ip参数
        m_strIpparaCfg = new HCNetSDK.NET_DVR_IPPARACFG();
        m_strIpparaCfg.write();
        Pointer lpIpParaConfig = m_strIpparaCfg.getPointer();
        bRet = hCNetSDK.NET_DVR_GetDVRConfig(lUserID, HCNetSDK.NET_DVR_GET_IPPARACFG, new NativeLong(0), lpIpParaConfig,
                m_strIpparaCfg.size(), ibrBytesReturned);
        m_strIpparaCfg.read();
        logger.info("设备信息:" + JSONUtil.toJsonStr(m_strDeviceInfo));

        if (!bRet) {
            // 设备不支持,则表示没有IP通道
            for (int iChannum = 0; iChannum < m_strDeviceInfo.byChanNum; iChannum++) {
                final Map<Integer, String> device = new HashMap<Integer, String>();
                int cNum = iChannum + m_strDeviceInfo.byStartChan;
                device.put(cNum, "Camera" + cNum);
                devices.add(device);
            }
        } else {
            for (int iChannum = 0; iChannum < HCNetSDK.MAX_IP_CHANNEL; iChannum++) {
                if (m_strIpparaCfg.struIPChanInfo[iChannum].byEnable == 1) {
                    final Map<Integer, String> device = new HashMap<Integer, String>();
                    int cNum = iChannum + m_strDeviceInfo.byStartChan + 32;
                    device.put(cNum, "IPCamera" + cNum);
                    devices.add(device);
                }
            }
        }
        return devices;
    }

    /**
     * 路线文件的查找
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param channel   通道号
     * @return
     */
    public List<Map<String, Object>> getFileList(final Date startTime, final Date endTime, final int channel) {// GEN-HEADEREND:event_jButtonSearchActionPerformed
        final List<Map<String, Object>> fileList = new ArrayList<Map<String, Object>>();
        // 搜索文件信息的
        HCNetSDK.NET_DVR_FILECOND m_strFilecond = new HCNetSDK.NET_DVR_FILECOND();
        m_strFilecond.struStartTime = getHkTime(startTime);
        m_strFilecond.struStopTime = getHkTime(endTime);
        m_strFilecond.lChannel = new NativeLong(channel);// 通道号
        m_strFilecond.dwFileType = 0;// 文件类型:0定时录像
        m_strFilecond.dwIsLocked = 0xff;// 0xff表示所有文件（包括锁定和未锁定）
        m_strFilecond.dwUseCardNo = 0; // 是否使用卡号
        // 查找文件
        NativeLong lFindFile = hCNetSDK.NET_DVR_FindFile_V30(lUserID, m_strFilecond);
        HCNetSDK.NET_DVR_FINDDATA_V30 strFile = new HCNetSDK.NET_DVR_FINDDATA_V30();
        // 是不是查找文件失败
        if (lFindFile.intValue() == -1) {
            int iErr = hCNetSDK.NET_DVR_GetLastError();
            logger.info("NET_DVR_FindFile_V30失败,错误号：" + iErr);
            return fileList;
        }
        NativeLong lnext;
        strFile = new HCNetSDK.NET_DVR_FINDDATA_V30();
        // 循环
        while (true) {
            // 获取下一个文件
            lnext = hCNetSDK.NET_DVR_FindNextFile_V30(lFindFile, strFile);
            if (lnext.intValue() == HCNetSDK.NET_DVR_FILE_SUCCESS) {
                Map<String, Object> newRow = new HashMap<String, Object>();
                // 添加文件名信息
                String[] s = new String[2];
                s = new String(strFile.sFileName).split("\0", 2);
                newRow.put("sFileName", new String(s[0]));
                int iTemp;
                String sFileSize;
                if (strFile.dwFileSize < 1024 * 1024) {
                    iTemp = (strFile.dwFileSize) / (1024);
                    sFileSize = iTemp + "K";
                } else {
                    iTemp = (strFile.dwFileSize) / (1024 * 1024);
                    sFileSize = iTemp + "M   ";
                    iTemp = ((strFile.dwFileSize) % (1024 * 1024)) / (1204);
                    sFileSize = sFileSize + iTemp + "K";
                }
                newRow.put("sFileSize", sFileSize); // 添加文件大小信息
                newRow.put("sFileStartTime", strFile.struStartTime.toStringTime());// 添加开始时间信息
                newRow.put("sFileStopTime", strFile.struStopTime.toStringTime()); // 添加结束时间信息

                fileList.add(newRow);
            } else {
                if (lnext.intValue() == HCNetSDK.NET_DVR_ISFINDING) {// 搜索中
                    logger.info("搜索中");
                    continue;
                } else {
                    if (lnext.intValue() == HCNetSDK.NET_DVR_FILE_NOFIND) {
                        logger.info("没有搜到文件");
                        break;
                    } else {
                        logger.info("搜索文件结束");
                        boolean flag = hCNetSDK.NET_DVR_FindClose_V30(lFindFile);
                        if (flag == false) {
                            logger.info("结束搜索失败");
                        }
                        break;
                    }
                }
            }
        }
        return fileList;
    }

    /**
     * 下载文件
     *
     * @param fileName
     */
    public int downloadByfile(final String fileName) {
        // 正在下载
        if (m_lDownloadHandle.intValue() >= 0) {
            logger.info("m_lDownloadHandle正在下载:" + m_lDownloadHandle);
            return -1;
        }
        // 设置文件名
        String sFileName = "c:/DownLoad/" + fileName;
        System.out.println(sFileName);
        // 暂且将文件名作为保存的名字
        m_lDownloadHandle = hCNetSDK.NET_DVR_GetFileByName(lUserID, sFileName, sFileName);
        logger.info("hksdk(视频)-获取播放句柄信息,状态值:" + hCNetSDK.NET_DVR_GetLastError());
        if (m_lDownloadHandle.intValue() < 0) {
            System.out.println("hksdk(视频)-下载失败" + hCNetSDK.NET_DVR_GetLastError());
            return -1;
        }
        // 开始播放
        boolean downloadFlag = hCNetSDK.NET_DVR_PlayBackControl(m_lDownloadHandle, HCNetSDK.NET_DVR_PLAYSTART, 0, null);
        if (!downloadFlag) {
            m_lDownloadHandle.setValue(-1);
            logger.info("play back control failed {}", hCNetSDK.NET_DVR_GetLastError());
            return -1;
        }

        // 获取下载进度
        int iPos = 0;
        for (iPos = 0; iPos < 100 && iPos >= 0; iPos = hCNetSDK.NET_DVR_GetDownloadPos(m_lDownloadHandle)) {
            logger.info("Be downloading... : {}%", iPos);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        logger.info("have got {}%\n", iPos);

        // 停止下载
        if (!hCNetSDK.NET_DVR_StopGetFile(m_lDownloadHandle)) {
            logger.info("failed to stop get file {} \n", hCNetSDK.NET_DVR_GetLastError());
            // 设置为没有下载
            m_lDownloadHandle.setValue(-1);
            return -1;
        }
        // 设置为没有下载
        m_lDownloadHandle.setValue(-1);
        // 判断是否下载成功
        if (iPos < 0 || iPos > 100) {
            logger.info("download err {}\n", hCNetSDK.NET_DVR_GetLastError());
            return -1;
        }
        return 0;
    }

    /**
     * 按照时间下载
     *
     * @param startTime
     * @param endTime
     * @param chanNum
     * @return
     */
    @Value("${mediaserver.path}")
    String sysPath;
    public String downloadByTime(String trialNo, String fileName, final int chanNum, final Date startTime, final Date endTime) {
        // 正在下载
        if (m_lDownloadHandle.intValue() >= 0) {
            logger.info("m_lDownloadHandle正在下载:" + m_lDownloadHandle);
            return "-1";
        }

        // 开始时间和结束时间
        HCNetSDK.NET_DVR_TIME struStartTime = getHkTime(startTime);
        HCNetSDK.NET_DVR_TIME struStopTime = getHkTime(endTime);
        // 设置文件名
        String sFileName = sysPath+"/" + trialNo + "/" + fileName;
        String tmpName = System.getProperty("user.dir") + "/" + fileName;
        File file = new File(sFileName);
        if (!file.exists()) file.getParentFile().mkdirs();
        System.out.println(sFileName);
        // 下载根据时间
        m_lDownloadHandle = hCNetSDK.NET_DVR_GetFileByTime(lUserID, new NativeLong(chanNum), struStartTime,
                struStopTime, tmpName);
        if (m_lDownloadHandle.intValue() < 0) {
            System.out.println("hksdk(视频)-下载失败:" + hCNetSDK.NET_DVR_GetLastError());
            return "-1";
        }
        // 开始下载
        if (!hCNetSDK.NET_DVR_PlayBackControl(m_lDownloadHandle, HCNetSDK.NET_DVR_PLAYSTART, 0, null)) {
            logger.error("Play back control failed {}", hCNetSDK.NET_DVR_GetLastError());
            // 设置为没有下载
            m_lDownloadHandle.setValue(-1);
            return "-1";
        }

        int nPos = 0;
        for (nPos = 0; nPos < 100 && nPos >= 0; nPos = hCNetSDK.NET_DVR_GetDownloadPos(m_lDownloadHandle)) {
            logger.info("Be downloading... {}%", nPos);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 停止下载
        if (!hCNetSDK.NET_DVR_StopGetFile(m_lDownloadHandle)) {
            logger.info("failed to stop get file {}\n", hCNetSDK.NET_DVR_GetLastError());
            // 设置为没有下载
            m_lDownloadHandle.setValue(-1);
            return "-1";
        }
        // 设置为没有下载
        m_lDownloadHandle.setValue(-1);
        //
        if (nPos < 0 || nPos > 100) {
            logger.info("download err {}\n", hCNetSDK.NET_DVR_GetLastError());
            return "-1";
        }
        logger.info("Be downloading... {}%\n", nPos);
        try {
//            解决下载的mp4无法被浏览器播放，不需要浏览器播放可去掉
            MediaTransferFlvByFFmpeg.convetor(tmpName, sFileName);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            File f = new File(tmpName);
            if (f.exists()) f.delete();
        }
        return trialNo + "/" + fileName;
    }

    /**
     * 获取海康录像机格式的时间
     *
     * @param time
     * @return
     */
    public HCNetSDK.NET_DVR_TIME getHkTime(final Date time) {
        HCNetSDK.NET_DVR_TIME structTime = new HCNetSDK.NET_DVR_TIME();
        String str = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(time);
        String[] times = str.split("-");
        structTime.dwYear = Integer.parseInt(times[0]);
        structTime.dwMonth = Integer.parseInt(times[1]);
        structTime.dwDay = Integer.parseInt(times[2]);
        structTime.dwHour = Integer.parseInt(times[3]);
        structTime.dwMinute = Integer.parseInt(times[4]);
        structTime.dwSecond = Integer.parseInt(times[5]);
        return structTime;
    }

}